package com.luanye.enjoy.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luanye.enjoy.constants.RedisConst;
import com.luanye.enjoy.constants.SystemConst;
import com.luanye.enjoy.dto.DynamicDTO;
import com.luanye.enjoy.dto.ScrollResult;
import com.luanye.enjoy.entity.*;
import com.luanye.enjoy.enums.ResultCode;
import com.luanye.enjoy.mapper.DynamicMapper;
import com.luanye.enjoy.mapper.TopicDynamicMapper;
import com.luanye.enjoy.mapper.TopicMapper;
import com.luanye.enjoy.service.*;
import com.luanye.enjoy.utils.BeanCopyUtils;
import com.luanye.enjoy.utils.CacheClient;
import com.luanye.enjoy.utils.SecurityUtils;
import com.luanye.enjoy.vo.RespBean;
import io.netty.util.concurrent.CompleteFuture;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.luanye.enjoy.constants.RedisConst.FEED_KEY;

@Service("dynamicService")
@AllArgsConstructor
public class DynamicServiceImpl extends ServiceImpl<DynamicMapper, Dynamic> implements DynamicService {

    private final StringRedisTemplate stringRedisTemplate;

    private final UserService userService;

    private final CacheClient cacheClient;

    private final TopicDynamicService topicDynamicService;

    private final TopicMapper topicMapper;

    private final TopicDynamicMapper topicDynamicMapper;

    private final CommentService commentService;

    private final FollowService followService;

    @Override
    public RespBean publish(DynamicDTO dto) {
        Dynamic dynamic = BeanCopyUtils.copyBean(dto, Dynamic.class);
        boolean isSuccess = save(dynamic);

        if (!isSuccess){
            return RespBean.error(ResultCode.PUBLISH_ERROR);
        }
        topicAndPushHandler(dto.getTopics(), dynamic.getId(), SecurityUtils.getUserId(), dynamic.getId());

        return RespBean.success(dynamic.getId());
    }


    @Async
    public void topicAndPushHandler(Integer[] topics, Integer did, Long userid, Integer dynamicId){
        Arrays.stream(topics)
                .forEach(id -> {
                    topicDynamicService.save(new TopicDynamic(id, did));
                });

        //获取粉丝
        List<Follow> follows = followService.query().eq("follow_user_id", userid).list();
        //推送
        for (Follow follow : follows){
            //获取粉丝id
            Long fansId = follow.getUserId();
            String key = FEED_KEY + fansId;
            stringRedisTemplate.opsForZSet().add(key, dynamicId.toString(), System.currentTimeMillis());
        }
    }

    @Override
    public RespBean like(Integer id) {
        Long userId = SecurityUtils.getUserId();

        String key = RedisConst.DYNAMIC_LIKE_KEY + id;
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());

        if (score == null)
        {
            //未喜欢
            boolean isSuccess = update().setSql("likes = likes + 1").eq("id", id).update();
            if (isSuccess){
                stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
            }
        }
        else
        {
            //喜欢
            boolean isSuccess = update().setSql("likes = likes - 1").eq("id", id).update();
            if (isSuccess){
                stringRedisTemplate.opsForZSet().remove(key, userId.toString());
            }
        }
        return RespBean.success();
    }


    private void isDynamicLiked(Dynamic dynamic){
        WeChatUser weChatUser = SecurityUtils.getWeChatUser();
        if (weChatUser == null){
            return;
        }
        Long userid = weChatUser.getUser().getId();
        String key = RedisConst.DYNAMIC_LIKE_KEY + dynamic.getId();

        Double score = stringRedisTemplate.opsForZSet().score(key, userid.toString());
        dynamic.setLiked(score != null);
    }

    private void queryBlogUser(Dynamic dynamic){
        Long userid = dynamic.getCreateBy();
        User user = userService.getById(userid);
        dynamic.setNickname(user.getNickname());
        dynamic.setAvatar(user.getAvatar());
        dynamic.setTitle(user.getTitle());
    }

    private void queryDynamicTopic(Dynamic dynamic){
        Integer id = dynamic.getId();
        //查找话题列表
        List<String> topicList = topicMapper.getTopicListByDynamicId(id);
        dynamic.setTopics(topicList);
    }

    @Override
    public RespBean queryHotDynamic(Integer current) {
        if (current == null)
            return RespBean.error(ResultCode.VALIDATE_ERROR);
        Page<Dynamic> page = query()
                .orderByDesc("likes")
                .page(new Page<>(current, SystemConst.MAX_PAGE_SIZE));
        
        List<Dynamic> records = page.getRecords();
        records.forEach(dynamic -> {
            this.queryBlogUser(dynamic);
            this.isDynamicLiked(dynamic);
            RespBean bean = commentService.getNum(Long.valueOf(dynamic.getId()));
            dynamic.setCommentNum(bean.getObj());
        });
        //Long userid = SecurityUtils.getUserId();
        //CompletableFuture<Void> userFuture = CompletableFuture.runAsync(() -> {
        //    records.forEach( dynamic -> this.queryBlogUser(dynamic, userid) );
        //});
        //
        //CompletableFuture<Void> likeFuture = CompletableFuture.runAsync(() -> {
        //    records.forEach( dynamic -> this.isDynamicLiked(dynamic, userid) );
        //});
        //
        //CompletableFuture<Void> topicFuture = CompletableFuture.runAsync(() -> {
        //    records.forEach( dynamic -> this.queryDynamicTopic(dynamic) );
        //});
        //
        //CompletableFuture<Void> future = CompletableFuture.allOf(userFuture, likeFuture, topicFuture);
        //future.join();
        return RespBean.success(records);
    }

    @Override
    public RespBean queryDynamicById(Integer id) {
        Dynamic dynamic = cacheClient.queryWithPassThrough(RedisConst.CACHE_DYNAMIC_KEY,
                id, Dynamic.class, this::getById, RedisConst.CACHE_DYNAMIC_TTL, TimeUnit.MINUTES);
        if (dynamic == null){
            return RespBean.error(ResultCode.DYNAMIC_NOT_EXISTED);
        }
        //Dynamic dynamic = getById(id);
        //if (dynamic == null){
        //
        //}
        //queryBlogUser(dynamic);
        //isDynamicLiked(dynamic);

        this.queryDynamicTopic(dynamic);
        this.isDynamicLiked(dynamic);
        this.queryBlogUser(dynamic);
        return RespBean.success(dynamic);
    }

    @Override
    public RespBean getMyDynamic() {
        Long userid = SecurityUtils.getUserId();
        LambdaQueryWrapper<Dynamic> query = new LambdaQueryWrapper<>();
        query.eq(Dynamic::getCreateBy, userid);
        query.orderByDesc(Dynamic::getCreateTime);
        List<Dynamic> result = list(query);
        return RespBean.success(result);
    }

    @Override
    public RespBean getDynamicByTopic(Integer topicId) {
        List<Integer> dynamicIds = topicDynamicMapper.getDynamicListByTid(topicId);
        List<Dynamic> result = dynamicIds.stream()
                .map(id -> {
                    Dynamic dynamic = getById(id);
                    this.queryBlogUser(dynamic);
                    this.isDynamicLiked(dynamic);
                    this.queryDynamicTopic(dynamic);
                    return dynamic;
                })
                .collect(Collectors.toList());
        return RespBean.success(result);
    }

    @Override
    public RespBean deleteById(Integer id) {
        Long userid = SecurityUtils.getUserId();
        LambdaQueryWrapper<Dynamic> query = new LambdaQueryWrapper<>();
        query.eq(Dynamic::getCreateBy, userid);
        query.eq(Dynamic::getId, id);

        int delete = baseMapper.delete(query);
        if (delete == 0){
            return RespBean.error(ResultCode.FAILED);
        }
        return RespBean.success();
    }

    @Override
    public RespBean queryBlogOfFollow(Long max, Integer offset) {

        Long userId = SecurityUtils.getUserId();

        String key = FEED_KEY + userId;

        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 10);
        // 3.非空判断
        if (typedTuples == null || typedTuples.isEmpty()) {
            return RespBean.success();
        }

        // 4.解析数据：blogId、minTime（时间戳）、offset
        List<Long> ids = new ArrayList<>(typedTuples.size());
        long minTime = 0; // 2
        int os = 1; // 2
        for (ZSetOperations.TypedTuple<String> tuple : typedTuples) { // 5 4 4 2 2
            // 4.1.获取id
            ids.add(Long.valueOf(tuple.getValue()));
            // 4.2.获取分数(时间戳）
            long time = tuple.getScore().longValue();
            if(time == minTime){
                os++;
            }else{
                minTime = time;
                os = 1;
            }
        }

        // 5.根据id查询blog
        String idStr = StrUtil.join(",", ids);
        List<Dynamic> dynamics = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();

        for (Dynamic dynamic : dynamics) {
            this.queryBlogUser(dynamic);
            this.isDynamicLiked(dynamic);
            RespBean bean = commentService.getNum(Long.valueOf(dynamic.getId()));
            dynamic.setCommentNum(bean.getObj());
        }

        // 6.封装并返回
        ScrollResult r = new ScrollResult();
        r.setList(dynamics);
        r.setOffset(os);
        r.setMinTime(minTime);

        return RespBean.success(r);
    }
}
